import os
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import math
sns.set_style("whitegrid")

# 读取数据
def load_iris():
    
    iris = pd.read_csv("../Datasets/iris/Iris.csv")
    # print(iris.head())
    # print(iris["Species"].value_counts())
    # # sns初始化
    # sns.set()
    # # 设置散点图x轴与y轴以及data参数
    # sns.relplot(x='SepalLengthCm', y='SepalWidthCm', hue='Species', style='Species', data=iris )
    # plt.title('SepalLengthCm and SepalWidthCm data analysize')
    # plt.show()

    # # 绘制特征分布
    # # 设置颜色主题
    # antV = ['#1890FF', '#2FC25B', '#FACC14', '#223273', '#8543E0', '#13C2C2', '#3436c7', '#F04864']
    # # 绘制  Violinplot
    # _, axes = plt.subplots(2, 2, figsize=(8, 8), sharex=True)
    # sns.despine(left=True)
    # sns.violinplot(x='Species', y='SepalLengthCm', data=iris, palette=antV, ax=axes[0, 0])
    # sns.violinplot(x='Species', y='SepalWidthCm', data=iris, palette=antV, ax=axes[0, 1])
    # sns.violinplot(x='Species', y='PetalLengthCm', data=iris, palette=antV, ax=axes[1, 0])
    # sns.violinplot(x='Species', y='PetalWidthCm', data=iris, palette=antV, ax=axes[1, 1])
    # plt.show()# 从小提琴图可以看到各个分类下的各个特征基本符合正态分布，所以假设其为正太分布

    data = np.array(iris)
    x, y = data[:,1:-1], data[:,-1]

    return x,y

# 模型定义
class NaiveBayes():
    def __init__(self,x,y):
        _,n = np.shape(x)
        self.y_unique = np.unique(y)
        l   = len(np.unique(self.y_unique))
        self.mean = np.zeros([l,n]) # 训练集各个特征在某一类下的均值
        self.var  = np.zeros([l,n]) # 训练集各个特征在某一类下的方差
        self.yprob= np.zeros([l])   # 训练集y的分布
        self.fit(x,y)

    def fit(self,x,y):

        data_slice = [ np.squeeze(x[np.argwhere(y==i)],axis=1) for i in self.y_unique ]
        for i in range(len(self.y_unique)):
            self.mean[i,:] = np.mean(data_slice[i],axis=0)
            self.var[i,:]  = np.var(data_slice[i],axis=0)
            self.yprob[i]  = np.shape(data_slice[i])[0] / len(y)

        return 'Complete！'

    def score(self,x,y):

        y_pred = []
        for i in range(len(y)):
            y_pred.append(self.pred(x[i]))

        total_num = len(y)
        corre_num = np.sum( (y_pred == y).astype(int) )
        acc = corre_num / total_num

        return acc

    def pred(self,x):

        prob = self.cal_prob(x)
        final_prob = np.ones([np.shape(prob)[0]])
        for i in range(np.shape(prob)[1]):
            final_prob = final_prob * prob[:,i]
        final_prob = final_prob * self.yprob
        max_index = np.argmax(final_prob)

        return self.y_unique[max_index]

    def cal_prob(self,x):
        
        x = x.astype(np.float_)
        exponent = np.exp( -( np.power(x - self.mean, 2)/(2 * self.var) ) )
        prob = (1 / (np.sqrt(2 * np.pi * self.var) )) * exponent

        return prob

# 主函数
def main():

    # 加载数据，打印基本信息
    x,y = load_iris()
    print('x Shape:{:<25s} y Shape:{:<25s}'.format(str(x.shape),str(y.shape)))
    print('x type :{:<25s} y type :{:<25s}'.format(str(type(x)),str(type(y))))

    # 数据切分
    x_train = np.concatenate((x[:40,:],  x[50:90,:], x[100:140,:]), axis=0)
    y_train = np.concatenate((y[:40],    y[50:90],   y[100:140]  ), axis=0)
    x_test  = np.concatenate((x[40:50,:],x[90:100,:],x[140:150,:]), axis=0)
    y_test  = np.concatenate((y[40:50],  y[90:100],  y[140:150]  ), axis=0)

    # 创建模型与训练
    bayes = NaiveBayes(x_train,y_train)
    
    # 测试
    ACC = bayes.score(x_test,y_test)
    print('ACC:',ACC)



if __name__ == '__main__':
    main()